package com.suray.rgv.son.mother.core;

import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.utils.LoggerType;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import com.suray.commin.core.utils.UUID;
import com.suray.commin.exception.Rgv60TaskException;
import com.suray.rgv.RgvSonInterface;
import com.suray.rgv.son.RgvSonImpl;
import com.suray.rgv.son.core.RgvSonInfo;
import com.suray.rgv.son.exception.SonRgvActionException;
import com.suray.rgv.son.mother.util.RgvSMLogger;
import com.suray.rgv.son.util.RgvSonActionResult;
import com.suray.rgv.util.ErrorInfo;
import com.suray.rgv60.util.Rgv60ErrorUtil;
import com.suray.type.Rgv60Status;
import com.suray.type.RgvSonTaskResult;
import com.suray.type.RgvSonTaskStatus;
import com.suray.wcs.res.enums.PalletStatus;
import com.suray.wcs.res.enums.RgvStatus;
import suray.rgv.communication.SeerCommManage;

import java.io.IOException;
import java.util.List;

/**
 * 子车控制
 */
public class RgvSonControl {
    //子车
    private RgvSonInterface sonRgv;

    //通讯管理
    private SeerCommManage seerComm;

    //序列号
    private String serialNum;

    //中断
    private boolean interrupt;

    private String groundCodeInMother;

    private Boolean rgvDirectionX;

    private Boolean rgvDirectionY;

    private RgvSMCore rgvSMCore;

    public RgvSonControl(RgvSMCore rgvSMCore, SeerCommManage seerComm, String serialNum) {
        this.rgvSMCore = rgvSMCore;
        this.seerComm = seerComm;
        this.serialNum = serialNum;
        this.interrupt = false;
    }

    public void init() {
        if (sonRgv != null) {
            sonRgv.init();
        }
    }

    /**
     * 子车按速度行驶
     * @param direction
     */
    public void sonRgvMoveBySpeed(Integer direction) {
        try {
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "按速度行驶", null, this.getClass());
            String taskId = UUID.randomUUID().toString();
            sonRgv.moveSpeedAction(taskId, direction);
        } catch (IOException e) {
            throw new RuntimeException("按速度行驶时，通讯异常");
        }
    }

    /**
     * 子车按速度停止
     */
    public void stopSonRgvMoveBySpeed() {
        try {
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "按速度停止", null, this.getClass());
            String taskId = UUID.randomUUID().toString();
            sonRgv.stopMoveBySpeed(taskId);
        } catch (IOException e) {
            throw new RuntimeException("按速度停止时，通讯异常");
        }
    }

    public void sonRgvMoveByDistance(Integer direction) {
        try {
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "按位移行驶", null, this.getClass());
            String taskId = UUID.randomUUID().toString();
            sonRgv.moveByDistance(taskId, direction);
        } catch (IOException e) {
            throw new RuntimeException("按位移行驶时，通讯异常");
        }
    }

    public void sonRgvRecovery() {
        try {
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "故障恢复", null, this.getClass());
            if (sonRgv.getActionResult() > 0) {
                RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "下发动作确认", null, this.getClass());
                sonRgv.actionResultConfirm();
            }
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "清除子车故障", null, this.getClass());
            sonRgv.clearError();
            sonRgv.clearTask();
            waitSonTaskClearFault();
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "子车故障恢复完成", null, this.getClass());
        } catch (IOException e) {
            throw new RuntimeException("故障恢复时，通讯异常");
        }
    }

    /**
     * 等待子车故障清除结束
     */
    private void waitSonTaskClearFault() throws IOException {
        RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "开始等待子车故障清除结束", null, this.getClass());
        int tick = 20;
        while (sonRgv.getErrorCode() != 0 || sonRgv.getWarnCode() != 0) {
            if (tick <= 0) {
                throw new SonRgvActionException("子车故障恢复失败");
            } else {
                tick--;
            }
            WcsFuncUtil.delay(1000);
        }
    }

    /**
     * 子车充电
     */
    public void sonRgvCharge() {
        try {
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "充电", null, this.getClass());
            String taskId = UUID.randomUUID().toString();
            sonRgv.charge(taskId);
            int tick = 5;
            while (sonRgv.getRgvStatus() != RgvStatus.CHARGING) {
                if (tick == 0) {
                    throw new RuntimeException("子车充电失败");
                } else {
                    tick--;
                }
                WcsFuncUtil.delay(1000);
            }
        } catch (IOException e) {
            throw new RuntimeException("下发子车充电时，通讯异常");
        }
    }

    /**
     * 子车停止充电
     */
    public void sonRgvStopCharge() {
        try {
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "停止充电", null, this.getClass());
            String taskId = UUID.randomUUID().toString();
            sonRgv.stopCharge(taskId);
            int tick = 5;
            while (sonRgv.getRgvStatus() != RgvStatus.READY) {
                if (tick == 0) {
                    throw new RuntimeException("子车停止充电失败");
                } else {
                    tick--;
                }
                WcsFuncUtil.delay(1000);
            }
        } catch (IOException e) {
            throw new RuntimeException("下发子车充电时，通讯异常");
        }
    }

    /**
     * 设置子车速度
     * @return
     */
    public void sonRgvChangeSpeedConfig(Integer moveSpeed, Integer moveSpeedAcc, Integer moveSpeedDec) {
        RgvSonInfo rgvSonInfo = getRgvSonInfoData();
        Integer oldMoveSpeed = (int) rgvSonInfo.getRunSpeedMove();
        Integer oldMoveSpeedAcc = (int) rgvSonInfo.getRunSpeedMoveAcc();
        Integer oldMoveSpeedDec = (int) rgvSonInfo.getRunSpeedMoveDec();
        sonRgv.changeSpeedConfig(moveSpeed, moveSpeedAcc, moveSpeedDec);
        try {
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "修改子车速度",
                    "行驶速度：" + moveSpeed + ", 加速度" + moveSpeedAcc + ", 减速度" + moveSpeedDec, this.getClass());
            sonRgv.setRgvSonSpeedScript();
        } catch (IOException e) {
            sonRgv.changeSpeedConfig(oldMoveSpeed, oldMoveSpeedAcc, oldMoveSpeedDec);
            throw new RuntimeException("修改子车速度时，通讯异常");
        } catch (Throwable e) {
            sonRgv.changeSpeedConfig(oldMoveSpeed, oldMoveSpeedAcc, oldMoveSpeedDec);
            throw e;
        }
    }

    /**
     * 子车校准
     */
    public void sonRgvCalibration() {
        try {
            RgvSMLogger.info(LoggerType.SON_RGV, serialNum, "校准", null, this.getClass());
            String taskId = UUID.randomUUID().toString();
            sonRgv.calibration(taskId);
        } catch (IOException e) {
            throw new RuntimeException("校准时，通讯异常");
        }
    }

    /**
     * 子车新任务
     * 这里只发送任务，具体任务进行到哪了，什么时候结束需要自行通过 完成段序号和任务结果 来判断
     * @param taskId
     * @param rgvSonTaskId
     * @param nodeList
     * @param pathIndexs
     * @param startTask
     * @throws IOException
     */
    public void doSonRgvTaskNew(Object taskId, int rgvSonTaskId, List<Node> nodeList, int[] pathIndexs,
                                boolean startTask) throws IOException {
        if (sonRgv.getRgvStatus() != RgvStatus.READY) {
            throw new RuntimeException(serialNum + "子车未就绪，不能执行任务");
        }

        sonRgv.sendTask(null, rgvSonTaskId, nodeList, pathIndexs, startTask);
    }

    /**
     * 等待子车动作完成
     * @param taskId
     * @param des
     */
    private void waitSonRgvTaskEnd(Object taskId, String des) {
        RgvSMLogger.info(LoggerType.INFO, serialNum, des, "等待子车动作执行完成:" + taskId,
                this.getClass());
        int count = 0;
        while (true) {
            checkInterrupt(des);
            count++;

            if (count >= 60) {
                if (rgvSMCore.getRgvStatus() == Rgv60Status.ERROR) {
                    throw new Rgv60TaskException("故障：" + rgvSMCore.getMaxError().getErrorMessage());
                }
            }

            if (sonRgv.isTaskEnd(taskId)) {
                break;
            }

            WcsFuncUtil.delay(200);
        }

        sonRgvConfirm(des);
        RgvSMLogger.info(LoggerType.INFO, serialNum, des, "子车动作执行完成:" + taskId,
                this.getClass());
    }

    /**
     * 子车动作结果确认
     * @param des
     */
    public void sonRgvConfirm(String des) {
        while (true) {
            checkInterrupt("子车动作结果确认:" + des);
            try {
                sonRgv.clearTask();
                break;
            } catch (Exception e) {
                RgvSMLogger.exceptionLog(LoggerType.GET_SON_RGV_INFO, serialNum, des, e, this.getClass());
            }

            WcsFuncUtil.delay(200);
        }

        int tick = 2;
        while (true) {
            checkInterrupt("子车动作结果确认, 等待完成:" + des);
            if (sonRgv.getActionResult() == RgvSonActionResult.INIT.getValue()) {
                break;
            }

            tick--;
            if (tick <= 0) {
                throw new RuntimeException("子车动作结果清除" + des + "失败");
            }

            WcsFuncUtil.delay(200);
        }
    }

    public int getActionResult() {
        return sonRgv.getActionResult();
    }

    public RgvStatus getRgvStatus() {
        if (sonRgv == null) {
            return RgvStatus.OFF_LINE;
        }

        return sonRgv.getRgvStatus();
    }

    public boolean isSonRgvStatic() {
        return sonRgv.isStatic();
    }

    public String getSeries() {
        return sonRgv.getSeries();
    }

    public void setInterrupt() {
        interrupt = true;
        sonRgv.rgvError(new ErrorInfo(Rgv60ErrorUtil.INTERRUPT_RGV.getCode(), Rgv60ErrorUtil.INTERRUPT_RGV.getDes(),
                Rgv60ErrorUtil.INTERRUPT_RGV.getLevel()));
    }

    public void clearInterrupt() {
        interrupt = false;
    }

    public void checkInterrupt(String des) {
        if (interrupt) {
            throw new RuntimeException(serialNum + "中断，" + des);
        }

        sonRgv.checkException();
    }

    public RgvSonInfo getRgvSonInfoData() {
        if (sonRgv == null) {
            return null;
        }

        return ((RgvSonImpl) sonRgv).getRgvSonInfoData();
    }

    public Integer getCurActionNo() {
        return sonRgv.getCurActionNo();
    }

    public boolean isSonRgvEnable() {
        if (sonRgv == null) {
            return false;
        }

        return sonRgv.isRgvEnable();
    }

    /**
     * 子车是否在母车里面
     * 通过光电判断, 如果子车在线还要判断子车扫到的条码是否在母车内
     * 子车条码是否需要添加有效时间，子车的数据不是实时获取
     * 如果子车条码过了有效时间，则忽略当前条码信息，重新获取子车信息，
     * 如果子车信息获取失败，忽略条码只判断光电信号 -- 感觉有问题，不做这个策略
     *
     * 如果子车停用后，母车可以随意移动
     * @return
     */
    public boolean isInMother() {
        if (!isSonRgvEnable()) {
            return true;
        }

        if (sonRgv.getRgvStatus() == RgvStatus.OFF_LINE) {
            RgvSMLogger.info(LoggerType.INFO, serialNum, "检测子车是否在母车内", "子车离线", this.getClass());
            return false;
        }

        return sonRgv.isInMother();
    }

    public boolean isLastGroundCodeInMother() {
        if (sonRgv == null) {
            return false;
        }

        return sonRgv.isInMother();
    }

    public String getGroundCode() {
        if (sonRgv == null) {
            return null;
        }

        return sonRgv.getGroundCode();
    }

    public String getSonGroundCodeInMother() {
        return sonRgv.getSonGroundCodeInMother();
    }

    public void setSonGroundCodeInMother(String sonGroundCodeInMother) {
        groundCodeInMother = sonGroundCodeInMother;
        if (sonRgv != null) {
            sonRgv.setPlatformGroundCode(groundCodeInMother);
        }
    }

    public void setRgvDirectionX(Boolean rgvDirection) {
        this.rgvDirectionX = rgvDirection;

        if (sonRgv != null) {
            sonRgv.setRgvDirectionX(rgvDirectionX);
        }
    }

    public void setRgvDirectionY(Boolean rgvDirection) {
        this.rgvDirectionY = rgvDirection;

        if (sonRgv != null) {
            sonRgv.setRgvDirectionY(rgvDirectionY);
        }
    }

    public PalletStatus getRgvPalletStatus() {
        if (sonRgv == null) {
            return PalletStatus.UNKNOWN;
        }
        return sonRgv.getRgvPalletStatus();
    }

    public Float getBatSoc() {
        return sonRgv.getBatSoc();
    }

    /**
     * 清除子车异常
     * @throws IOException
     */
    public void clearError() throws IOException {
        clearInterrupt();
        sonRgv.recovery();
    }

    /**
     * 子车在线
     * @return
     */
    public boolean isOnline() {
        return sonRgv.isOnline();
    }

    /**
     * 启用子车
     */
    public void enableSonRgv() {
        sonRgv.enableRgv();
    }

    /**
     * 停用子车
     */
    public void disableSonRgv() {
        sonRgv.disableRgv();
    }

    public Integer getRgvCurTaskId() {
        return sonRgv.getRgvCurTaskId();
    }

    public Integer getRgvTaskEndSegment() {
        return sonRgv.getRgvTaskEndSegment();
    }

    public RgvSonTaskResult getRgvSonTaskResult() {
        return sonRgv.getRgvSonTaskResult();
    }

    public RgvSonTaskStatus getRgvSonTaskStatus() {
        return sonRgv.getRgvSonTaskStatus();
    }

    public long getRgvSonDataTime() {
        return sonRgv.getRgvSonDataTime();
    }

    public int getErrorCode() {
        return sonRgv.getErrorCode();
    }

    public int getTaskErrorCode() {
        return sonRgv.getRgvSonTaskErrorCode();
    }

    public void palletUp(Object taskId) throws IOException {
        sonRgv.palletUp(taskId);
    }

    public void palletDown(Object taskId) throws IOException {
        sonRgv.palletDown(taskId);
    }

    /**
     * 更改子车
     * @param rgvSon
     */
    public void changeSonRgv(Object rgvSon) {
        if (rgvSon instanceof RgvSonInterface) {
            sonRgv = (RgvSonInterface) rgvSon;
            sonRgv.setPlatformGroundCode(groundCodeInMother);
            sonRgv.setRgvDirectionX(rgvDirectionX);
            sonRgv.setRgvDirectionY(rgvDirectionY);
        } else {
            throw new RuntimeException("子车对象错误，无法更改子车:" + rgvSon);
        }
    }

    public boolean palletCalibration() throws IOException {
        sonRgv.palletCalibration(null);
        return true;
    }

    public void taskStart() throws IOException {
        sonRgv.taskStart();
    }
}
